package com.cherry.androidviewdemo.widget

import android.graphics.Rect
import android.support.v7.widget.RecyclerView
import android.util.Log
import android.util.SparseArray
import kotlin.math.max
import kotlin.math.roundToInt

/**
 * RecyclerView的回收原则
 * 自定义的LayoutManager中只需要告诉RecyclerView哪些HolderView已经不用了即可（使用removeAndRecycleView(view, recycler)函数）
 * mCachedViews是第一级缓存，它的size为2，只能保存两个HolderView。这里保存的始终是最新鲜被移除的HolderView，
 * 当mCachedViews满了以后，会利用先进先出原则，把老的HolderView存放在mRecyclerPool中。在mRecyclerPool中，它的默认size是5
 *
 * detachAndScrapAttachedViews(recycler);只会被用在onLayoutChildren函数中
 * onLayoutChildren函数是用来布局新的Item的，只有在布局时，才会先把HolderView detach掉然后再add进来重新布局。
 * 但大家需要注意的是mAttachedScrap中存储的就是新布局前从RecyclerView中剥离下来的当前在显示的Item的holderView。这些holderView并不参与回收复用。
 * 单纯只是为了先从RecyclerView中拿下来，再重新布局上去。对于新布局中没有用到的HolderView，会从mAttachedScrap移到mCachedViews中，让它参与复用
 *
 * 要从RecyclerView中拿到一个HolderView用来布局，我们一般是使用recycler.getViewForPosition(int position)
 *
 * int getItemCount()  得到的是Adapter中总共有多少数据要显示，也就是总共有多少个item
 * int getChildCount()  得到的是当前RecyclerView在显示的item的个数
 * View getChildAt(int position) 获取某个可见位置的View，需要非常注意的是，它的位置索引并不是Adapter中的位置索引，而是当前在屏幕上的位置的索引
 * int getPosition(View view) 得到某个View在Adapter中的索引位置
 *
 * @author DongMS
 * @since 2020/5/21
 */
class CustomLayoutManager : RecyclerView.LayoutManager() {

    private var sumDy = 0

    private var realHeight = 0

    private var itemRects = SparseArray<Rect>()

    override fun generateDefaultLayoutParams(): RecyclerView.LayoutParams {
        //没什么特殊需求的话，则可以直接让子item自己决定自己的宽高即可（wrap_content）
        return RecyclerView.LayoutParams(RecyclerView.LayoutParams.WRAP_CONTENT, RecyclerView.LayoutParams.WRAP_CONTENT)
    }

    override fun onLayoutChildren(recycler: RecyclerView.Recycler, state: RecyclerView.State) {
        val itemCount = itemCount
        if (itemCount == 0) {
            detachAndScrapAttachedViews(recycler)
            return
        }
        detachAndScrapAttachedViews(recycler)

        //定义垂直方向的偏移量
        var offset = 0

        //没有复用的
//        for (i in 0 until itemCount) {
//            val view = recycler.getViewForPosition(i)
//            addView(view)
//            measureChildWithMargins(view, 0, 0)
//            //通过getDecoratedMeasuredWidth(view)得到的是item+decoration的总宽度。如果你只想得到view的测量宽度，通过view.getMeasuredWidth()就可以得到了
//            val width = getDecoratedMeasuredWidth(view)
//            val height = getDecoratedMeasuredHeight(view)
//            layoutDecorated(view, 0, offset, width, offset + height)
//            offset += height
//        }

        //测算单个item的高度和屏幕可见的数量
        val itemView = recycler.getViewForPosition(0)
        measureChildWithMargins(itemView, 0, 0)
        val itemWidth = getDecoratedMeasuredWidth(itemView)
        val itemHeight = getDecoratedMeasuredHeight(itemView)
        val visibleCount = (getVerticalSpace() * 1f / itemHeight).roundToInt()

        for (i in 0 until itemCount) {
            val rect = Rect()
            rect.set(0, offset, itemWidth, offset + itemHeight)
            itemRects.put(i, rect)
            offset += itemHeight
        }

        for (i in 0 until visibleCount) {
            val rect = itemRects[i]
            val view = recycler.getViewForPosition(i)
            addView(view)
            measureChildWithMargins(view, 0, 0)//先measure
            layoutDecorated(view, rect.left, rect.top, rect.right, rect.bottom)//再layout
        }
        realHeight = max(offset, getVerticalSpace())

    }

    /**
     * 得到的是RecyclerView用于显示的高度
     */
    private fun getVerticalSpace() = height - paddingTop - paddingBottom

    //使LayoutManager具有横向滚动的功能，可以通过在canScrollHorizontally()中return true;
    override fun canScrollVertically(): Boolean {
        return true
    }

    /**
     * dy表示手指在屏幕上每次滑动的位移.
    当手指由下往上滑时,dy>0
    当手指由上往下滑时,dy<0
     */
    override fun scrollVerticallyBy(dy: Int, recycler: RecyclerView.Recycler, state: RecyclerView.State): Int {
        val childCount = childCount
        if (childCount <= 0) {
            return dy
        }

        //到顶的边界判断
        var travel = dy
        if (sumDy + dy < 0) {
            travel = -sumDy
        } else if (sumDy + dy > realHeight - getVerticalSpace()) {
            //到底边界判断
            travel = realHeight - getVerticalSpace() - sumDy
        }


        //判断哪些需要回收的item
        for (i in childCount - 1 downTo 0) {
            val child = getChildAt(i)
            if (travel > 0 && child != null) {
                //  当手指由下往上滑时,dy>0时
                //滑动到顶部被移除的view
                if (getDecoratedBottom(child) - travel < 0) {
                    removeAndRecycleView(child, recycler)
                    continue
                }
            } else if (travel < 0 && child != null) {
                //  当手指由上往下滑时,dy<0时
                //滑动到底部被移除的view
                if (getDecoratedTop(child) - travel > height - paddingBottom) {
                    removeAndRecycleView(child, recycler)
                    continue
                }
            }
        }

        val scrollVisibleArea = getScrollVisibleArea(travel)
        if (travel >= 0) {
            //  当手指由下往上滑时,dy>0时
            val lastView = getChildAt(childCount - 1) ?: return dy
            val minPos = getPosition(lastView) + 1 // view总体向上，从底部最后一个view开始

            for (i in minPos until itemCount) {
                val itemRect = itemRects[i]
                //判断这个item是不是在这个显示区域内
                if (Rect.intersects(scrollVisibleArea, itemRect)) {
                    val view = recycler.getViewForPosition(i)
                    addView(view)
                    measureChildWithMargins(view, 0, 0)
                    layoutDecorated(view, itemRect.left, itemRect.top - sumDy, itemRect.right, itemRect.bottom - sumDy)
                } else {
                    break
                }
            }
        } else {
            //  当手指由上往下滑时,dy<0时
            val firstView = getChildAt(0) ?: return dy
            val maxPos = getPosition(firstView) - 1
            for (i in maxPos downTo 0) {
                val itemRect = itemRects[i]
                //判断这个item是不是在这个显示区域内
                if (Rect.intersects(scrollVisibleArea, itemRect)) {
                    val view = recycler.getViewForPosition(i)
                    addView(view, 0)//将View添加至RecyclerView中，childIndex为1，但是View的位置还是由layout的位置决定
                    measureChildWithMargins(view, 0, 0)
                    layoutDecorated(view, itemRect.left, itemRect.top - sumDy, itemRect.right, itemRect.bottom - sumDy)
                } else {
                    break
                }
            }
        }


        sumDy += travel
        Log.e("-----", "移动距离=$travel,手指滑动的距离=$dy")
        offsetChildrenVertical(-travel)
        return dy
    }

    /**
     * 新增travel时，对应的屏幕位置
     */
    private fun getScrollVisibleArea(travel: Int) = Rect(paddingLeft, paddingTop + sumDy + travel, width + paddingRight, getVerticalSpace() + sumDy + travel)

}